package com.legendframework.core.dao.wrapper.defaults;

import com.legendframework.core.dao.Return;
import com.legendframework.core.dao.exception.DaoException;
import com.legendframework.core.dao.support.ClassUtils;
import com.legendframework.core.dao.utils.DaoStringUtils;
import com.legendframework.core.dao.wrapper.AbstractWrapper;
import com.legendframework.core.dao.wrapper.enums.Operator;
import com.legendframework.core.support.lang3.BooleanUtils;
import com.legendframework.core.support.lang3.StringUtils;
import com.legendframework.core.support.lang3.math.NumberUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.*;
import java.util.function.Function;

public abstract class AbstractDefaultWrapper<T, C, R extends AbstractWrapper<T, List<WrapperResult>, C, R>> extends AbstractWrapper<T, List<WrapperResult>, C, R> {

    private final R typedThis = (R) this;

    /**
     * 当前与上一句条件的衔接符号是And还是Or
     */
    private Operator orOrAnd = Operator.AND;

    public AbstractDefaultWrapper() {
        result = new ArrayList<>();
    }

    @Override
    public R eq(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.EQ, column, val);
    }

    @Override
    public R ne(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.NE, column, val);
    }

    @Override
    public R gt(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.GT, column, val);
    }

    @Override
    public R ge(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.GE, column, val);
    }

    @Override
    public R lt(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.LT, column, val);
    }

    @Override
    public R le(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.LE, column, val);
    }

    @Override
    public R in(boolean condition, C column, Object... vals) {
        return addCondition(condition, Operator.IN, column, vals);
    }

    @Override
    public R notIn(boolean condition, C column, Object... vals) {
        return addCondition(condition, Operator.NOT_IN, column, vals);
    }

    @Override
    public R like(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.LIKE, column, val);
    }

    @Override
    public R notLike(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.NOT_LIKE, column, val);
    }

    @Override
    public R likeLeft(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.LLIKE, column, val);
    }

    @Override
    public R likeRight(boolean condition, C column, Object val) {
        return addCondition(condition, Operator.RLIKE, column, val);
    }

    @Override
    public R isNull(boolean condition, C column) {
        return addCondition(condition, Operator.NULL, column);
    }

    @Override
    public R isNotNull(boolean condition, C column) {
        return addCondition(condition, Operator.NOT_NULL, column);
    }

    @Override
    protected R addCondition(boolean condition, Operator operator, C column, Object... vals) {
        if (result.size() > 0) {
            if (orOrAnd == Operator.AND) {
                doIt(true, Operator.AND, null, null);
            } else if (orOrAnd == Operator.OR) {
                doIt(true, Operator.OR, null, null);
            }
        }
        return doIt(condition, operator, column, null, vals).and();
    }

    protected R doIt(boolean condition, Operator operator, C column, List<WrapperResult> wrapperResults, Object... vals) {
        if (condition) {
            WrapperResult wrapperResult = new WrapperResult();
            wrapperResult.setOperator(operator);
            wrapperResult.setColumn(getColumn(column));
            wrapperResult.setVals(vals);
            wrapperResult.setWrapperResults(wrapperResults);
            result.add(wrapperResult);
        }
        return typedThis;
    }

    @Override
    public R or(boolean condition) {
        orOrAnd = Operator.OR;
        return typedThis;
    }

    @Override
    public R and(boolean condition) {
        orOrAnd = Operator.AND;
        return typedThis;
    }

    @Override
    public R and(boolean condition, Function<R, R> func) {
        if (!condition) {
            return typedThis;
        }
        R andR = func.apply(instance());
        doIt(true, Operator.AND, null, null);
        return doIt(condition, Operator.BRACKET, null, andR.getWrapperResult());
    }

    @Override
    public R or(boolean condition, Function<R, R> func) {
        if (!condition) {
            return typedThis;
        }
        R andR = func.apply(instance());
        doIt(true, Operator.OR, null, null);
        return doIt(condition, Operator.BRACKET, null, andR.getWrapperResult());
    }

    @Override
    public Return<String,Object[]> toSql() {
        //占位符参数
        List<Object> params = new ArrayList<>();
        //为递归做准备的闭包函数
        Function<List<WrapperResult>,String> toSql = new Function<List<WrapperResult>,String>() {
            /**
             * 将一个条件构造器转换为SQL语句 (不喊WHERE前缀)
             * @param s 条件构造器
             * @return
             */
            @Override
            public String apply(List<WrapperResult> s) {
                StringBuilder sb = new StringBuilder();
                for (WrapperResult res : s) {
                    Operator operator = res.getOperator();
                    if (operator != Operator.AND && operator != Operator.OR && operator != Operator.BRACKET) {
                        if (operator == Operator.NULL || operator == Operator.NOT_NULL) {
                            sb.append(" ").append(res.getColumn()).append(" ").append(operator.getXpression());
                        } else if((operator == Operator.IN || operator == Operator.NOT_IN) && res.getVals() != null && res.getVals().length > 0){
                            sb.append(" ").append(res.getColumn()).append(" ").append(operator.getXpression()).append(" ( ");
                            for (int i = 0; i < res.getVals().length; i++) {
                                if (i > 0) {
                                    sb.append(",");
                                }
                                Object val = res.getVals()[i];
                                sb.append(" ? ");
                                params.add(val);
                            }
                            sb.append(" ) ");
                        } else if (operator == Operator.LLIKE){
                            sb.append(" ").append(res.getColumn()).append(" LIKE ").append(" '%?' ");
                            params.add(res.getVals()[0]);
                        } else if (operator == Operator.RLIKE){
                            sb.append(" ").append(res.getColumn()).append(" LIKE ").append(" '?%' ");
                            params.add(res.getVals()[0]);
                        } else if (operator == Operator.LIKE || operator == Operator.NOT_LIKE){
                            sb.append(" ").append(res.getColumn()).append(" ").append(operator.getXpression()).append(" '%?%' ");
                            params.add(res.getVals()[0]);
                        } else {
                            sb.append(" ").append(res.getColumn()).append(" ").append(operator.getXpression()).append(" ? ");
                            params.add(res.getVals()[0]);
                        }
                    } else if (operator == Operator.AND || operator == Operator.OR) {
                        sb.append(" ").append(operator.getXpression());
                    } else {
                        sb.append(" ( ").append(this.apply(res.getWrapperResults())).append(" ) ");
                    }
                }
                return sb.toString();
            }
        };

        //SQL语句
        String sql = toSql.apply(result);

        return new Return<String, Object[]>() {
            @Override
            public String getR1() {
                return (DaoStringUtils.isEmpty(sql) ? "" : " WHERE ") + sql;
            }

            @Override
            public Object[] getR2() {
                return params.toArray(new Object[0]);
            }
        };
    }

    @Override
    public boolean check(T t) {
        return check(t, result);
    }

    @Override
    public String toExpression() {
        return toExpression(result);
    }

    /**
     * 递归将条件转换为文本表达式
     *
     * 多数用于DEBUG
     *
     * @param wrapper 条件结果集
     * @return
     */
    private String toExpression(List<WrapperResult> wrapper){
        StringBuilder str = new StringBuilder();
        for (WrapperResult res : wrapper) {
            Operator operator = res.getOperator();
            if (operator != Operator.AND && operator != Operator.OR && operator != Operator.BRACKET) {
                if (operator == Operator.NULL) {
                    str.append(" ").append("${").append(res.getColumn()).append("}").append(" == NULL");
                } else if (operator == Operator.NOT_NULL) {
                    str.append(" ").append("${").append(res.getColumn()).append("}").append(" != NULL");
                } else if(operator == Operator.IN || operator == Operator.NOT_IN){
                    for (int i = 0; i < res.getVals().length; i++) {
                        Object val = res.getVals()[i];
                        if (i != 0) {
                            str.append(" ").append(Operator.AND);
                        }
                        str.append(" ").append("${").append(res.getColumn()).append("}").append(operator == Operator.IN ? Operator.EQ.getXpression() : Operator.NE.getXpression()).append(" ").append(val);
                    }
                } else {
                    str.append(" ").append("${").append(res.getColumn()).append("} ").append(operator.getXpression()).append(" ").append(res.getVals()[0]);
                }
            } else if (operator == Operator.AND || operator == Operator.OR) {
                str.append(" ").append(operator.getXpression());
            } else {
                str.append(" ( ").append(toExpression(res.getWrapperResults())).append(" ) ");
            }
        }
        return str.toString();
    }

    /**
     * 校验这个条件器的最终比较结果
     * <p>
     * 会递归
     *
     * @param t       实体类对象
     * @param wrapper 条件
     * @return 是否符合
     */
    private boolean check(T t, List<WrapperResult> wrapper) {
        List<WrapperFormat> wrapperFormats = new ArrayList<>();
        for (WrapperResult wrapperResult : wrapper) {
            Operator operator = wrapperResult.getOperator();
            if (operator != Operator.AND && operator != Operator.OR && operator != Operator.BRACKET) {
                wrapperFormats.add(new WrapperFormat(null, checkOneField(t, wrapperResult)));
            } else if (operator == Operator.AND || operator == Operator.OR) {
                wrapperFormats.add(new WrapperFormat(operator, null));
            } else {
                wrapperFormats.add(new WrapperFormat(null, check(t, wrapperResult.getWrapperResults())));
            }
        }
        return checkFormat(wrapperFormats);
    }

    /**
     * 校验format
     *
     * @param wrapperFormats 布尔值的表达式
     * @return
     */
    private boolean checkFormat(List<WrapperFormat> wrapperFormats) {
        //生成布尔表达式 例: true and false or (true or false) 执行最终结果
        StringBuilder str = new StringBuilder();
        for (WrapperFormat wrapper : wrapperFormats) {
            if (wrapper.getOperator() == null) {
                str.append(" ").append(wrapper.getBol()).append(" ");
            } else {
                str.append(" ").append(wrapper.getOperator() == Operator.AND ? "&&" : "||").append(" ");
            }
        }
        ScriptEngine script = new ScriptEngineManager().getEngineByName("JavaScript");
        try {
            Object eval = script.eval(str.toString());
            return BooleanUtils.toBoolean(String.valueOf(eval));
        } catch (ScriptException e) {
            throw new DaoException(String.format("布尔表达式计算失败: %s", str.toString()), e);
        }
    }

    /**
     * 校验这个实体类的这个字段的值与预定值的比较
     *
     * @param t       实体类
     * @param wrapper 条件对象
     * @return 是否符合
     */
    private boolean checkOneField(T t, WrapperResult wrapper) {
        Object fieldValue = ClassUtils.getFieldValue(t, wrapper.getColumn());
        //NULL AND NOT_NULL
        if (fieldValue == null && wrapper.getOperator() == Operator.NULL) {
            return true;
        } else if (fieldValue != null && wrapper.getOperator() == Operator.NOT_NULL) {
            return true;
        } else if (fieldValue == null) {
            return false;
        }
        //LIKE NOT_LIKE AND LLIKE AND RLIKE
        if (wrapper.getOperator() == Operator.LIKE) {
            return String.valueOf(fieldValue).contains(String.valueOf(wrapper.getVals()[0]));
        }
        if (wrapper.getOperator() == Operator.NOT_LIKE) {
            return !String.valueOf(fieldValue).contains(String.valueOf(wrapper.getVals()[0]));
        }
        if (wrapper.getOperator() == Operator.LLIKE) {
            return String.valueOf(fieldValue).endsWith(String.valueOf(wrapper.getVals()[0]));
        }
        if (wrapper.getOperator() == Operator.RLIKE) {
            return String.valueOf(fieldValue).startsWith(String.valueOf(wrapper.getVals()[0]));
        }
        //EQ NE
        if (wrapper.getOperator() == Operator.EQ) {
            return DaoStringUtils.equals(String.valueOf(fieldValue),String.valueOf(wrapper.getVals()[0]));
        }
        if (wrapper.getOperator() == Operator.NE){
            return !DaoStringUtils.equals(String.valueOf(fieldValue),String.valueOf(wrapper.getVals()[0]));
        }
        //GT GE LT LE
        if (wrapper.getOperator() == Operator.GT || wrapper.getOperator() == Operator.GE || wrapper.getOperator() == Operator.LT || wrapper.getOperator() == Operator.LE) {
            if (fieldValue.getClass() == Integer.class || fieldValue.getClass() == int.class ||
                    fieldValue.getClass() == Short.class || fieldValue.getClass() == short.class ||
                    fieldValue.getClass() == Character.class || fieldValue.getClass() == char.class ||
                    fieldValue.getClass() == Byte.class || fieldValue.getClass() == byte.class ||
                    fieldValue.getClass() == Float.class || fieldValue.getClass() == float.class ||
                    fieldValue.getClass() == Double.class || fieldValue.getClass() == double.class ||
                    fieldValue.getClass() == Long.class || fieldValue.getClass() == long.class) {
                try {
                    return operator(NumberUtils.toDouble(String.valueOf(fieldValue)), wrapper.getOperator(), Double.valueOf(String.valueOf(wrapper.getVals()[0])));
                } catch (NumberFormatException e) {
                    throw new DaoException(String.format("条件值与字段实际值不匹配,无法进行转换, 字段类型: %s , 实际值: %s", fieldValue.getClass().getName(), wrapper.getVals()[0]));
                }
            }else {
                if (StringUtils.equals(String.valueOf(fieldValue), String.valueOf(wrapper.getVals()[0])) && (wrapper.getOperator() == Operator.GE || wrapper.getOperator() == Operator.LE)) {
                    return true;
                }
                //利用ASCI码排序字符串进行比较大小
                Set<String> tree = new TreeSet<>();
                tree.add(String.valueOf(fieldValue));
                tree.add(String.valueOf(wrapper.getVals()[0]));
                for (String s : tree) {
                    if (wrapper.getOperator() == Operator.GT || wrapper.getOperator() == Operator.GE) {
                        return StringUtils.equals(s , String.valueOf(wrapper.getVals()[0]));
                    }else {
                        return StringUtils.equals(s , String.valueOf(fieldValue));
                    }
                }
            }
        }
        //IN NOT_IN
        if (wrapper.getOperator() == Operator.IN) {
            return Arrays.asList(wrapper.getVals()).contains(fieldValue);
        }
        if (wrapper.getOperator() == Operator.NOT_IN) {
            return !Arrays.asList(wrapper.getVals()).contains(fieldValue);
        }
        return false;
    }

    /**
     * 四种条件比较器
     * <p>
     * 《 》 《= 》=
     *
     * @param fieldValue 对象字段的值
     * @param operator   比较表达式
     * @param checkValue 比较的值
     * @return
     */
    private boolean operator(Double fieldValue, Operator operator, Double checkValue) {
        switch (operator) {
            case GT:
                return fieldValue > checkValue;
            case GE:
                return fieldValue >= checkValue;
            case LT:
                return fieldValue < checkValue;
            case LE:
                return fieldValue <= checkValue;
            default:
                return false;
        }
    }

    /**
     * 字段转换为String
     *
     * @param column 字段
     * @return string类型的字段
     */
    abstract String getColumn(C column);
}
